Monad 函子
 1. Monad 函子是可以变扁的Pointed函子(即：解决IO函子中嵌套的问题)
 2. 一个函子如果具有join和of两个方法并遵守一些定律就是一个Monad

 const fs = require('fs')
 const fp = require('loadsh/fp')

 class IO {
   static of (value) {
     return new IO(function () {
       return value
     })
   }

   constructor (fn) {
     this._value = fn
   }

   map (fn) {
     return new IO(fp.flowRight(fn, this._value))
   }

   join () {
     return this._value()
   }

   flatMap (fn) {
    return this.map(fn).join()
   }
 }

 let readFile = function (filename) {
   return new IO(function() {
     return fs.readFileSync(filename, 'utf-8')
   })
 }

 let print = function(x) {
   return new IO(function(){
     console.log(x)
     return x
   })
 }

 let r = readFile('package.json')
          .map(fp.toUpper)
          .flatMap(print)
          .join()

console.log(r)
